#include "native/java/lang/System.hpp"
#include "rtda/heap/class.h"

namespace jvm {
    namespace lang {

        const std::string SystemNative::JL_SYSTEM = "java/lang/System";

        void SystemNative::arraycopy(std::shared_ptr<rtda::Frame> frame) {
            auto& vars = frame->getLocalVars();
            auto src = vars.getRef(0);
            auto srcPos = vars.getInt(1);
            auto dest = vars.getRef(2);
            auto destPos = vars.getInt(3);
            auto length = vars.getInt(4);

            if (src == nullptr || dest == nullptr) {
                LOG_ERROR("NullPointerException");
                throw std::runtime_error("java.lang.NullPointerException");
            }
            if (!checkArrayCopy(src, dest)) {
                LOG_ERROR("ArrayStoreException");
                throw std::runtime_error("java.lang.ArrayStoreException");
            }
            if (srcPos < 0 || destPos < 0 || length < 0 ||
                srcPos + length > src->arrayLength() ||
                destPos + length > dest->arrayLength()) {
                LOG_ERROR("IndexOutOfBoundsException");
                throw std::runtime_error("java.lang.IndexOutOfBoundsException");
            }

            heap::Object::arrayCopy(src, dest, srcPos, destPos, length);
        }

        bool SystemNative::checkArrayCopy(heap::ObjectPtr src, heap::ObjectPtr dest) {
            auto srcClass = src->getClass().lock();
            auto destClass = dest->getClass().lock();

            if (!srcClass->isArray() || !destClass->isArray()) {
                return false;
            }
            if (srcClass->componentClass()->isPrimitive() ||
                destClass->componentClass()->isPrimitive()) {
                return srcClass == destClass;
            }
            return true;
        }

    } // namespace lang
} // namespace jvm